package org.snaker.engine.spring;

import com.zaxxer.hikari.HikariDataSource;
import org.snaker.engine.Completion;
import org.snaker.engine.Expression;
import org.snaker.engine.service.IProcessService;
import org.snaker.engine.service.IQueryService;
import org.snaker.engine.service.ITaskService;
import org.snaker.engine.SnakerEngine;
import org.snaker.engine.interceptors.SnakerInterceptor;
import org.snaker.engine.TaskAccessStrategy;
import org.snaker.engine.access.dialect.PageDialect;
import org.snaker.engine.access.dialect.SQLServerPageDialect;
import org.snaker.engine.cache.memory.MemoryCacheManager;
import org.snaker.engine.core.BasicSnakerEngine;
import org.snaker.engine.service.impl.ManagerService;
import org.snaker.engine.service.impl.OrderService;
import org.snaker.engine.service.impl.ProcessService;
import org.snaker.engine.service.impl.QueryService;
import org.snaker.engine.service.impl.TaskService;
import org.snaker.engine.impl.GeneralAccessStrategy;
import org.snaker.engine.impl.GeneralCompletion;
import org.snaker.engine.impl.JuelExpression;
import org.snaker.engine.parser.ModelParser;
import org.snaker.engine.parser.NodeParserProvider;
import org.snaker.engine.parser.xml.XMLModelParser;
import org.snaker.engine.parser.xml.XmlNodeParserProvider;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.JdkRegexpMethodPointcut;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.support.lob.DefaultLobHandler;
import org.springframework.jdbc.support.lob.LobHandler;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource;
import org.springframework.transaction.interceptor.TransactionInterceptor;
import org.w3c.dom.Element;

import javax.sql.DataSource;
import java.util.List;
import java.util.Properties;

@Configuration
public class SnakerConfiguration {

    @Bean
    public XmlNodeParserProvider nodeParserProvider() {
        return new XmlNodeParserProvider();
    }

    @Bean
    public Expression expression() {
        return new JuelExpression();
    }

    @Bean
    public ModelParser modelParser(NodeParserProvider<Element> nodeParserProvider) {
        return new XMLModelParser(nodeParserProvider);
    }

    @Bean
    Completion completion() {
        return new GeneralCompletion();
    }

    @Bean
    public ProcessService processService(
            MemoryCacheManager memoryCacheManager,
            SpringJdbcAccess springJdbcAccess,
            ModelParser modelParser
    ) {
        //TODO
        ProcessService processService = new ProcessService(modelParser, springJdbcAccess);
        processService.setCacheManager(memoryCacheManager);
        return processService;
    }

    @Bean
    public OrderService orderService(SpringJdbcAccess springJdbcAccess, Completion completion, IQueryService queryService, ITaskService taskService) {
        return new OrderService(springJdbcAccess, completion, queryService, taskService);
    }

    @Bean
    public TaskAccessStrategy accessStrategy() {
        return new GeneralAccessStrategy();
    }

    @Bean
    public TaskService taskService(SpringJdbcAccess springJdbcAccess, TaskAccessStrategy accessStrategy, IProcessService processService, Completion completion) {
        return new TaskService(springJdbcAccess, accessStrategy, processService, completion);
    }

    @Bean
    public ManagerService managerService(SpringJdbcAccess springJdbcAccess) {
        return new ManagerService(springJdbcAccess);
    }

    @Bean
    public QueryService queryService(SpringJdbcAccess springJdbcAccess) {
        return new QueryService(springJdbcAccess);
    }

    @Bean
    public MemoryCacheManager memoryCacheManager() {
        return new MemoryCacheManager();
    }

    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        // 基于 JDBC 的事务管理器
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean
    public PageDialect pageDialect() {
        return new SQLServerPageDialect();
    }

    @Bean
    @DependsOn("dataSource")
    public SpringJdbcAccess springJdbcAccess(DataSource dataSource, LobHandler lobHandler, PageDialect pageDialect) {
        SpringJdbcAccess access = new SpringJdbcAccess(pageDialect);
        access.setDataSource(dataSource);
        access.setLobHandler(lobHandler);
        return access;
    }

    @Bean
    public TransactionInterceptor transactionInterceptor(org.springframework.transaction.PlatformTransactionManager transactionManager) {
        NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();

        // 定义事务规则
        Properties attributes = new Properties();

        // 写操作事务，使用 PROPAGATION_REQUIRED
        attributes.setProperty("start*", "PROPAGATION_REQUIRED,-Throwable");
        attributes.setProperty("save*", "PROPAGATION_REQUIRED,-Throwable");
        attributes.setProperty("delete*", "PROPAGATION_REQUIRED,-Throwable");
        attributes.setProperty("update*", "PROPAGATION_REQUIRED,-Throwable");
        attributes.setProperty("execute*", "PROPAGATION_REQUIRED,-Throwable");
        attributes.setProperty("complete*", "PROPAGATION_REQUIRED,-Throwable");

        // 只读操作事务，优化性能
        attributes.setProperty("get*", "PROPAGATION_REQUIRED,readOnly");
        attributes.setProperty("find*", "PROPAGATION_REQUIRED,readOnly");

        // 默认事务规则
        attributes.setProperty("*", "PROPAGATION_REQUIRED");

        source.setProperties(attributes);

        // 创建事务拦截器
        return new TransactionInterceptor(transactionManager, source);
    }

    // 定义切入点，拦截目标方法
    @Bean
    public JdkRegexpMethodPointcut transactionPointcut() {
        JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut();
        // 匹配规则：拦截 org.snaker.engine.service 包及其子包中的所有方法
        pointcut.setPattern("org.snaker.engine.service.*.*");
        return pointcut;
    }

    // 将切入点和事务拦截器绑定到切面
    @Bean
    public DefaultPointcutAdvisor transactionAdvisor(TransactionInterceptor transactionInterceptor, JdkRegexpMethodPointcut transactionPointcut) {
        return new DefaultPointcutAdvisor(transactionPointcut, transactionInterceptor);
    }

    // 自动代理生成器，用于代理增强的目标类
    @Bean
    public DefaultAdvisorAutoProxyCreator autoProxyCreator() {
        return new DefaultAdvisorAutoProxyCreator();
    }

    @Bean
    public SnakerEngine snakerEngine(ProcessService processService, OrderService orderService, TaskService taskService, ManagerService managerService, QueryService queryService, List<SnakerInterceptor> interceptors, Expression expression) {
        SnakerEngine engine = new BasicSnakerEngine(processService, orderService, taskService, queryService, managerService, interceptors, expression);
        return engine;
    }

    @Bean
    public SpringConfiguration springConfiguration(ApplicationContext context) {
        return new SpringConfiguration(context);
    }

    @Bean
    public DataSource dataSource(Environment env) {
        HikariDataSource dataSource = new HikariDataSource();
        dataSource.setJdbcUrl(env.getProperty("spring.datasource.url"));
        dataSource.setUsername(env.getProperty("spring.datasource.username"));
        dataSource.setPassword(env.getProperty("spring.datasource.password"));
        dataSource.setDriverClassName(env.getProperty("spring.datasource.driver-class-name"));
        dataSource.setMaximumPoolSize(env.getProperty("spring.datasource.hikari.maximum-pool-size", Integer.class));
        dataSource.setMinimumIdle(env.getProperty("spring.datasource.hikari.minimum-idle", Integer.class));
        dataSource.setConnectionTimeout(env.getProperty("spring.datasource.hikari.connection-timeout", Long.class));
        dataSource.setIdleTimeout(env.getProperty("spring.datasource.hikari.idle-timeout", Long.class));
        dataSource.setMaxLifetime(env.getProperty("spring.datasource.hikari.max-lifetime", Long.class));
        dataSource.setConnectionTestQuery(env.getProperty("spring.datasource.hikari.connection-test-query"));
        dataSource.setPoolName(env.getProperty("spring.datasource.hikari.pool-name"));
        return dataSource;
    }

    @Bean
    public LobHandler lobHandler() {
        return new DefaultLobHandler();
    }


}
